package com.codeanalysissys.backend.ruby;

import ghaffarian.graphs.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Paths;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.codeanalysissys.backend.ruby.parser.RubyLexer;
import com.codeanalysissys.backend.ruby.parser.RubyParser;
import com.codeanalysissys.backend.ruby.parser.RubyParserBaseVisitor;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ParseTree;
import com.codeanalysissys.backend.graphs.cfg.CFNode;
import com.codeanalysissys.backend.graphs.cfg.CFPathTraversal;
import com.codeanalysissys.backend.graphs.cfg.ControlFlowGraph;
import com.codeanalysissys.backend.graphs.pdg.DDEdge;
import com.codeanalysissys.backend.graphs.pdg.DataDependenceGraph;
import com.codeanalysissys.backend.graphs.pdg.PDNode;
import ghaffarian.nanologger.Logger;
public class RubyDDGBuilder {
    private static String currentFile;


    private static Map<String, RubyClass> allClassInfos;

    private static Map<String, List<MethodDefInfo>> methodDEFs;

    public static DataDependenceGraph build(InputStream inFile) throws IOException {
        ANTLRInputStream input = new ANTLRInputStream(inFile);
        RubyLexer lexer = new RubyLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        RubyParser parser = new RubyParser(tokens);
        ParseTree parseTree = parser.compilation_unit();
        Logger.info("Done.");

        allClassInfos = new HashMap<>();
        List<RubyClass[]> filesClasses = new ArrayList<>();
        List<Rubymethod[]> filesmainmethod = new ArrayList<>();
        // extract all the information from PythonMethod and PythonClass
        List<RubyClass> classesList = RubyClassExtractor.extractInfo(parseTree);
        List<Rubymethod> mainMethodlist = RubyMethodExtractor.extractInfo(parseTree);
        filesClasses.add(classesList.toArray(new RubyClass[classesList.size()]));
        filesmainmethod.add(mainMethodlist.toArray(new Rubymethod[mainMethodlist.size()]));
        for (RubyClass cls : classesList)
            allClassInfos.put(cls.name, cls);
        methodDEFs = new HashMap<>();
        for (RubyClass[] classArray : filesClasses) {
            for (RubyClass cls : classArray) {
                for (Rubymethod mtd : cls.getAllMethods()) {
                    List<MethodDefInfo> list = methodDEFs.get(mtd.NAME);
                    if (list == null) {
                        list = new ArrayList<>();
                        list.add(new MethodDefInfo(mtd.RETURN_TYPE, mtd.NAME, cls.name, mtd.ARGUMENTS));//对应重载函数
                        methodDEFs.put(mtd.NAME, list);
                    } else {
                        list.add(new MethodDefInfo(mtd.RETURN_TYPE, mtd.NAME, cls.name, mtd.ARGUMENTS));
                        // no need to do 'methodDEFs.put(...)' again
                    }
                }
            }
        }
        for (Rubymethod[] mdlist : filesmainmethod) {
            for (Rubymethod md : mdlist) {
                List<MethodDefInfo> list = methodDEFs.get(md.NAME);
                if (list == null) {
                    list = new ArrayList<>();
                    list.add(new MethodDefInfo(md.RETURN_TYPE, md.NAME, null, md.ARGUMENTS));
                    methodDEFs.put(md.NAME, list);
                } else {
                    list.add(new MethodDefInfo(md.RETURN_TYPE, md.NAME, null, md.ARGUMENTS));
                }
            }
        }
        DataDependenceGraph ddg = new DataDependenceGraph();
        Map<ParserRuleContext, Object> pdNode = new IdentityHashMap<>();
        boolean changed;
        int iteration = 0;
        // walk the parseTree to analyse the def-use information(core)
        do {
            ++iteration;
            changed = false;
            DefUseVisitor defUse = new DefUseVisitor(iteration, filesClasses.get(0), ddg, pdNode);
            defUse.visit(parseTree);
            changed |= defUse.changed;

        } while (changed);
        ControlFlowGraph cfg = RubyCFGBuilder.build(parseTree, "pdnode", pdNode);
//		Logger.info("Done.");
//		// Finally, traverse all control-flow paths and draw data-flow dependency edges ...
//		Logger.info("\nAdding data-flow edges ... ");
        addDataFlowEdges(cfg, ddg);
        ddg.attachCFG(cfg);
        return ddg;
    }

    private enum analyseType {
        DEF,
        USE
    }

    public static DataDependenceGraph buildForOne(String fileName, InputStream inputStream) throws IOException {
        DataDependenceGraph[] ddgs = buildForAll(fileName, inputStream);
        return ddgs[0];
    }
    public static DataDependenceGraph[] buildForAll(String fileName,InputStream inputStream) throws IOException{
        int[] files=new int[1];
        // Parse all Java source files
        Logger.info("Parsing all source files ... ");
        ParseTree[] parseTrees = new ParseTree[files.length];
        for (int i = 0; i < files.length; ++i) {
            InputStream inFile = inputStream;
            ANTLRInputStream input = new ANTLRInputStream(inFile);
            RubyLexer lexer = new RubyLexer(input);
            CommonTokenStream tokens = new CommonTokenStream(lexer);
            RubyParser parser = new RubyParser(tokens);
            parseTrees[i] = parser.compilation_unit();
        }
        Logger.info("Done.");
        allClassInfos = new HashMap<>();
        List<RubyClass[]> filesClasses = new ArrayList<>();
        List<Rubymethod[]> filesmainmethod = new ArrayList<>();
        for (int i = 0; i < files.length; ++i) {
            // extract all the information from PythonMethod and PythonClass
            List<RubyClass> classesList = RubyClassExtractor.extractInfo(fileName, parseTrees[i]);
            List<Rubymethod> mainMethodlist = RubyMethodExtractor.extractInfo(fileName, parseTrees[i]);
            filesClasses.add(classesList.toArray(new RubyClass[classesList.size()]));
            filesmainmethod.add(mainMethodlist.toArray(new Rubymethod[mainMethodlist.size()]));
            for (RubyClass cls : classesList)
                allClassInfos.put(cls.name, cls);
        }
        methodDEFs = new HashMap<>();
        for (RubyClass[] classArray : filesClasses) {
            for (RubyClass cls : classArray) {
                for (Rubymethod mtd : cls.getAllMethods()) {
                    List<MethodDefInfo> list = methodDEFs.get(mtd.NAME);
                    if (list == null) {
                        list = new ArrayList<>();
                        list.add(new MethodDefInfo(mtd.RETURN_TYPE, mtd.NAME, cls.name, mtd.ARGUMENTS));//对应重载函数
                        methodDEFs.put(mtd.NAME, list);
                    } else {
                        list.add(new MethodDefInfo(mtd.RETURN_TYPE, mtd.NAME, cls.name, mtd.ARGUMENTS));
                        // no need to do 'methodDEFs.put(...)' again
                    }
                }
            }
        }
        for (Rubymethod[] mdlist : filesmainmethod) {
            for (Rubymethod md : mdlist) {
                List<MethodDefInfo> list = methodDEFs.get(md.NAME);
                if (list == null) {
                    list = new ArrayList<>();
                    list.add(new MethodDefInfo(md.RETURN_TYPE, md.NAME, null, md.ARGUMENTS));
                    methodDEFs.put(md.NAME, list);
                } else {
                    list.add(new MethodDefInfo(md.RETURN_TYPE, md.NAME, null, md.ARGUMENTS));
                }
            }
        }
        DataDependenceGraph[] ddgs = new DataDependenceGraph[files.length];
        for (int i = 0; i < ddgs.length; ++i)
            ddgs[i] = new DataDependenceGraph(fileName);
        Map<ParserRuleContext, Object>[] pdNodes = new Map[parseTrees.length];
        for (int i = 0; i < parseTrees.length; ++i)
            pdNodes[i] = new IdentityHashMap<>();
        boolean changed;
        int iteration = 0;
        // walk the parseTree to analyse the def-use information(core)
        do {
            ++iteration;
            changed = false;
            for (int i = 0; i < files.length; ++i) {
                currentFile = fileName;
                DefUseVisitor defUse = new DefUseVisitor(iteration, filesClasses.get(i), ddgs[i], pdNodes[i]);
                defUse.visit(parseTrees[i]);
                changed |= defUse.changed;
            }

        } while (changed);
        ControlFlowGraph[] cfgs = new ControlFlowGraph[files.length];
        for (int i = 0; i < files.length; ++i)
            cfgs[i] = RubyCFGBuilder.build(fileName, parseTrees[i], "pdnode", pdNodes[i]);
//		Logger.info("Done.");
//		// Finally, traverse all control-flow paths and draw data-flow dependency edges ...
//		Logger.info("\nAdding data-flow edges ... ");
        for (int i = 0; i < files.length; ++i) {
            addDataFlowEdges(cfgs[i], ddgs[i]);
            ddgs[i].attachCFG(cfgs[i]);
        }

        return ddgs;
    }
    public static DataDependenceGraph[] buildForAll(File[] files) throws IOException{
        Logger.info("Parsing all source files ... ");
        ParseTree[] parseTrees = new ParseTree[files.length];
        for (int i = 0; i < files.length; ++i) {
            CharStream input = CharStreams.fromPath(Paths.get(files[i].getAbsolutePath()));
            RubyLexer lexer = new RubyLexer(input);
            CommonTokenStream tokens = new CommonTokenStream(lexer);
            RubyParser parser = new RubyParser(tokens);
            parseTrees[i] = parser.compilation_unit();
        }
        Logger.info("Done.");
        allClassInfos = new HashMap<>();
        List<RubyClass[]> filesClasses = new ArrayList<>();
        List<Rubymethod[]> filesmainmethod = new ArrayList<>();
        for (int i = 0; i < files.length; ++i) {
            // extract all the information from PythonMethod and PythonClass
            List<RubyClass> classesList = RubyClassExtractor.extractInfo(files[i].getPath(), parseTrees[i]);
            List<Rubymethod> mainMethodlist = RubyMethodExtractor.extractInfo(files[i].getPath(), parseTrees[i]);
            filesClasses.add(classesList.toArray(new RubyClass[classesList.size()]));
            filesmainmethod.add(mainMethodlist.toArray(new Rubymethod[mainMethodlist.size()]));
            for (RubyClass cls : classesList)
                allClassInfos.put(cls.name, cls);
        }
        methodDEFs = new HashMap<>();
        for (RubyClass[] classArray : filesClasses) {
            for (RubyClass cls : classArray) {
                for (Rubymethod mtd : cls.getAllMethods()) {
                    List<MethodDefInfo> list = methodDEFs.get(mtd.NAME);
                    if (list == null) {
                        list = new ArrayList<>();
                        list.add(new MethodDefInfo(mtd.RETURN_TYPE, mtd.NAME, cls.name, mtd.ARGUMENTS));//对应重载函数
                        methodDEFs.put(mtd.NAME, list);
                    } else {
                        list.add(new MethodDefInfo(mtd.RETURN_TYPE, mtd.NAME, cls.name, mtd.ARGUMENTS));
                        // no need to do 'methodDEFs.put(...)' again
                    }
                }
            }
        }
        for (Rubymethod[] mdlist : filesmainmethod) {
            for (Rubymethod md : mdlist) {
                List<MethodDefInfo> list = methodDEFs.get(md.NAME);
                if (list == null) {
                    list = new ArrayList<>();
                    list.add(new MethodDefInfo(md.RETURN_TYPE, md.NAME, null, md.ARGUMENTS));
                    methodDEFs.put(md.NAME, list);
                } else {
                    list.add(new MethodDefInfo(md.RETURN_TYPE, md.NAME, null, md.ARGUMENTS));
                }
            }
        }
        DataDependenceGraph[] ddgs = new DataDependenceGraph[files.length];
        for (int i = 0; i < ddgs.length; ++i)
            ddgs[i] = new DataDependenceGraph(files[i].getName());
        Map<ParserRuleContext, Object>[] pdNodes = new Map[parseTrees.length];
        for (int i = 0; i < parseTrees.length; ++i)
            pdNodes[i] = new IdentityHashMap<>();
        boolean changed;
        int iteration = 0;
        // walk the parseTree to analyse the def-use information(core)
        do {
            ++iteration;
            changed = false;
            for (int i = 0; i < files.length; ++i) {
                currentFile = files[i].getName();
                DefUseVisitor defUse = new DefUseVisitor(iteration, filesClasses.get(i), ddgs[i], pdNodes[i]);
                defUse.visit(parseTrees[i]);
                changed |= defUse.changed;
            }

        } while (changed);
        ControlFlowGraph[] cfgs = new ControlFlowGraph[files.length];
        for (int i = 0; i < files.length; ++i)
            cfgs[i] = RubyCFGBuilder.build(files[i].getName(), parseTrees[i], "pdnode", pdNodes[i]);
//		Logger.info("Done.");
//		// Finally, traverse all control-flow paths and draw data-flow dependency edges ...
//		Logger.info("\nAdding data-flow edges ... ");
        for (int i = 0; i < files.length; ++i) {
            addDataFlowEdges(cfgs[i], ddgs[i]);
            ddgs[i].attachCFG(cfgs[i]);
        }

        return ddgs;
    }
    private static void addDataFlowEdges(ControlFlowGraph cfg, DataDependenceGraph ddg) {
        Set<CFNode> visitedDefs = new LinkedHashSet<>();
        for (CFNode entry: cfg.getAllMethodEntries()) {
            visitedDefs.clear();
            CFPathTraversal defTraversal = new CFPathTraversal(cfg, entry);
            while (defTraversal.hasNext()) {
                CFNode defCFNode = defTraversal.next();
                if (!visitedDefs.add(defCFNode)) {
                    defTraversal.continueNextPath();
                    continue;
                }
                PDNode defNode = (PDNode) defCFNode.getProperty("pdnode");
                if (defNode == null) {
                    //Logger.debug("No PDNode: " + defCFNode);
                    continue;
                }
                if (defNode.getAllDEFs().length == 0)
                    continue;
                // first add any self-flows of this node
                for (String flow: defNode.getAllSelfFlows()) {
                    ddg.addEdge(new Edge<>(defNode, new DDEdge(DDEdge.Type.FLOW, flow), defNode));
                }
                // now traverse the CFG for any USEs till a DEF
                Set<CFNode> visitedUses = new LinkedHashSet<>();
                for (String def: defNode.getAllDEFs()) {
                    CFPathTraversal useTraversal = new CFPathTraversal(cfg, defCFNode);
                    visitedUses.clear();
                    CFNode useCFNode = useTraversal.next(); // skip start node
                    visitedUses.add(useCFNode);
                    while (useTraversal.hasNext()) {
                        useCFNode = useTraversal.next();
                        PDNode useNode = (PDNode) useCFNode.getProperty("pdnode");
                        if (useNode == null) {
                            //Logger.debug("No PDNode: " + useCFNode);
                            continue;
                        }
                        if (useNode.hasDEF(def))
                            useTraversal.continueNextPath(); // no need to continue this path
                        if (!visitedUses.add(useCFNode))
                            useTraversal.continueNextPath(); // no need to continue this path
                        else
                        if (useNode.hasUSE(def))
                            ddg.addEdge(new Edge<>(defNode, new DDEdge(DDEdge.Type.FLOW, def), useNode));
                    }
                }
            }
        }
    }
    private static int countSlashes(String str) {
        int slashCount = 0;
        for (char chr: str.toCharArray())
            if (chr == '/')
                ++slashCount;
        return slashCount;
    }

    private static class DefUseVisitor extends RubyParserBaseVisitor<String>{
        private static final int PARAM = 1;
        private static final int FIELD = 101;
        private static final int LOCAL = 202;
        private static final int OUTER = 303;
        private static final int GLOBAL=404;
        private static final int MAIN=505;
        private int iteration;
        private boolean changed;
        private boolean Done;
        private boolean isfor = false;
        private boolean analysisVisit;
        private RubyClass[] classInfos;
        private DataDependenceGraph ddg;
        private Set<String> defList, useList, selfFlowList;
        private Map<ParserRuleContext, Object> pdNodes;
        private Deque<RubyClass> activeClasses;
        private MethodDefInfo methodDefInfo;
        private RubyField[] methodParams;
        private List<RubyField> localVars;
        private String expr;
        private String modifier = "public";
        private List<String> stmtLocalVars = new ArrayList<>();
        private List<RubyField> mainVars = new ArrayList<>();
        private List<RubyField> globalVars = new ArrayList<>();
        private List<String> stmtUses = new ArrayList<>();
        private boolean isSelf = false;
        private boolean isclass = false;
        private boolean ismethod = false;
        private boolean def = false;
        private boolean use = false;
        private boolean isglobal = false;
        private String selfadd = "";
        private String defexp = "";

        private String getOriginalCodeText(ParserRuleContext ctx) {
            int start = ctx.start.getStartIndex();
            int stop = ctx.stop.getStopIndex();
            Interval interval = new Interval(start, stop);
            return ctx.start.getInputStream().getText(interval);
        }

        public DefUseVisitor(int iter, RubyClass[] classInfos,
                             DataDependenceGraph ddg, Map<ParserRuleContext, Object> pdNodes) {
            expr = "";
            Done = false;
            this.ddg = ddg;
            changed = false;
            iteration = iter;
            analysisVisit = false;
            this.pdNodes = pdNodes;
            this.classInfos = classInfos;
            defList = new LinkedHashSet<>();
            useList = new LinkedHashSet<>();
            selfFlowList = new LinkedHashSet<>();
            activeClasses = new ArrayDeque<>();
            methodDefInfo = null;
            methodParams = new RubyField[0];
            localVars = new ArrayList<>();                    // localVars and for try and so on
        }
        private void analyseDefUse(PDNode node) {
            Logger.debug("--- ANALYSIS ---");
            Logger.debug(node.toString());
//            analysisVisit = true;
//            String expr = expression;
//            analysisVisit = false;
            for (String def: defList) {
                int status = isDefined(def);
                if (status > -1) {
                    if (status < 100) {
                        methodDefInfo.setArgDEF(status, true);
                        Logger.debug("Method defines argument #" + status);
                    } else if (status == FIELD) {
                        methodDefInfo.setStateDEF(true);
                        if (def.startsWith("this."))
                            def = def.substring(5);
                        def = "$THIS." + def;
                        Logger.debug("Method defines object state.");
                    }
                    changed |= node.addDEF(def);
                }
                else
                    Logger.debug(def + " is not defined!");
            }
            Logger.debug("Changed = " + changed);
            Logger.debug("DEFs = " + Arrays.toString(node.getAllDEFs()));
            //
            for (String use: useList) {
                int status = isDefined(use);
                if (status > -1) {
                    if (status == FIELD) {
                        if (use.startsWith("this."))
                            use = use.substring(5);
                        use = "$THIS." + use;
                    }
                    changed |= node.addUSE(use);
                } else
                    Logger.debug(use + " is not defined!");
            }
            Logger.debug("Changed = " + changed);
            Logger.debug("USEs = " + Arrays.toString(node.getAllUSEs()));
            //
            for (String flow: selfFlowList) {
                int status = isDefined(flow);
                if (status > -1) {
                    if (status == FIELD) {
                        if (flow.startsWith("this."))
                            flow = flow.substring(5);
                        flow = "$THIS." + flow;
                    }
                    changed |= node.addSelfFlow(flow);
                } else
                    Logger.debug(flow + " is not defined!");
            }
            Logger.debug("Changed = " + changed);
            Logger.debug("SELF_FLOWS = " + Arrays.toString(node.getAllSelfFlows()));
            defList.clear();
            useList.clear();
            selfFlowList.clear();
            Logger.debug("----------------");
        }

        private boolean isUsableExpression(String expr)
        {
            if(expr.startsWith("\"")&&expr.endsWith("\""))
                return false;
            if (expr.endsWith(")"))
                return false;
            // must not be an array-indexing expression



//            if (expr.endsWith("]"))
//                return false;



            // must not be post unary operation expression
            if (expr.endsWith("++") || expr.endsWith("--"))
                return false;
            // must not be a pre unary operation expression
            if (expr.startsWith("+") || expr.startsWith("-") || expr.startsWith("!") || expr.startsWith("~"))
                return false;
            // must not be an array initialization expression
            if (expr.endsWith("}"))
                return false;
            // must not be an explicit generic invocation expression
            if (expr.startsWith("<"))
                return false;
            //
            if (expr.startsWith(","))
                return false;
            //
            if (expr.endsWith(","))
                return false;
            //
            if(expr.equals(""))
                return false;
            return true;
        }
        private int isDefined(String id) {
            // return the defined method params
            if(!isclass&&!ismethod)
            {
                for (RubyField local : mainVars)
                    if (local.NAME.equals(id))
                        return MAIN;
            }
            else {
                for (int i = 0; i < methodParams.length; ++i)
                    if (methodParams[i].NAME.equals(id))
                        return i;
                // return the defined local var
                for (RubyField local : localVars)
                    if (local.NAME.equals(id))
                        return LOCAL;
                if (id.startsWith("self."))
                    id = id.substring(5);
                if (activeClasses != null && activeClasses.size() != 0) {
                    for (RubyField field : activeClasses.peek().getAllFields())
                        if (field.NAME.equals(id))
                            return FIELD;
                    for (RubyClass cls : activeClasses)
                        for (RubyField field : cls.getAllFields())
                            if (field.NAME.equals(id))
                                return OUTER;
                }
            }
            for (RubyField global : globalVars)
                if (global.NAME.equals(id))
                    return GLOBAL;
            return -1;
        }

        private MethodDefInfo findDefInfo(String name, String type, RubyField[] params) {
            List<MethodDefInfo> infoList = methodDEFs.get(name);
            // if exist the same name then distinguish by the other elements
            if (infoList.size() > 1 && infoList != null) {
                forEachInfo:
                for (MethodDefInfo info : infoList) {
                    if (info.CLASS_NAME != null) {
                        if (!info.CLASS_NAME.equals(activeClasses.peek().name))
                            continue;
                    }
                    if ((info.RET_TYPE == null && type != null) ||
                            (info.RET_TYPE != null && type == null))
                        continue;
                    if (type != null && !type.startsWith(info.RET_TYPE))
                        continue;
                    if (info.argDEFs().length!=params.length )
                        continue;
                    return info;
                }
            } else
            if (infoList.size() == 1)
                return infoList.get(0);
            return null;
        }

        @Override
        public String visitPrimaryBlockFor(RubyParser.PrimaryBlockForContext ctx)
        {
            PDNode forexp;
            def=true;
            use=false;
            visit(ctx.expr());
            defList.add(ctx.expr().getText());
            if(iteration==1)
            {
                forexp=new PDNode();
                forexp.setLineOfCode(ctx.expr().getStart().getLine());
                forexp.setCode("for "+getOriginalCodeText(ctx.expr())+" in "+getOriginalCodeText(ctx.when_cond()));
                analyseDefUse(forexp);
                ddg.addVertex(forexp);
                pdNodes.put(ctx,forexp);
            }
            else
                forexp=(PDNode) pdNodes.get(ctx);
            def=false;
            use=false;
            visit(ctx.statement_list_terms());
            def=false;
            use=false;
            Done=true;
            return null;
        }
        @Override
        public String visitExprBlock(RubyParser.ExprBlockContext ctx)
        {
            PDNode condition ;
            visit(ctx.expr());
            if(iteration==1)
            {
                condition=new PDNode();
                condition.setLineOfCode(ctx.expr().getStart().getLine());
                condition.setCode(getOriginalCodeText(ctx.expr()));
                analyseDefUse(condition);
                ddg.addVertex(condition);
                pdNodes.put(ctx.expr(),condition);
            }
            else
                condition=(PDNode)pdNodes.get(ctx.expr());
            use=false;
            def=false;
            if(ctx.block().block_params()!=null)
            {
                condition.addDEF(getOriginalCodeText(ctx.block().block_params().expr(0)));
                RubyField id;
                if(isclass)
                    id = new RubyField(modifier, getOriginalCodeText(ctx.block().block_params().expr(0)));
                else
                    id = new RubyField(null, getOriginalCodeText(ctx.block().block_params().expr(0)));
                if (!isclass&&!ismethod)
                    mainVars.add(id);
                else
                    localVars.add(id);
                PDNode params;
                if(iteration==1)
                {
                    params=new PDNode();
                    params.setLineOfCode(ctx.block().block_params().getStart().getLine());
                    params.setCode(getOriginalCodeText(ctx.block().block_params()));
                    params.addUSE(getOriginalCodeText(ctx.block().block_params().expr(0)));
                    analyseDefUse(params);
                    ddg.addVertex(params);
                    pdNodes.put(ctx.block().block_params(),params);
                }
                else
                    params=(PDNode)pdNodes.get(ctx.block().block_params());
            }
            def=false;
            use=false;
            visit(ctx.block().statement_list_terms());
            expNode(ctx.block().statement_list_terms());
            def=false;
            use=false;
            Done=true;
            return null;
        }
        @Override
        public String visitPrimaryFunctionCall0(RubyParser.PrimaryFunctionCall0Context ctx)
        {
            def=false;
            use=true;
            PDNode id;
            visit(ctx.func_call_parameters());
//            if(iteration==1)
//            {
//                id=new PDNode();
//                id.setLineOfCode(ctx.getStart().getLine());
//                id.setCode(getOriginalCodeText(ctx));
//                analyseDefUse(id);
//                ddg.addVertex(id);
//                pdNodes.put(ctx,id);
//            }
//            else
//                id=(PDNode) pdNodes.get(ctx);
            def=false;
            use=false;
            return null;
        }
        @Override
        public String visitExprWitStatementSuffix(RubyParser.ExprWitStatementSuffixContext ctx)
        {
            if(ctx.expr_statement_suffix().DO()==null) {//正常倒装if
                PDNode condition ;
                PDNode exp;
                use = true;
                def = false;
                visit(ctx.expr_statement_suffix().expr());
                if(iteration==1)
                {
                    condition=new PDNode();
                    condition.setLineOfCode(ctx.expr_statement_suffix().getStart().getLine());
                    condition.setCode(getOriginalCodeText(ctx.expr_statement_suffix()));
                    analyseDefUse(condition);
                    ddg.addVertex(condition);
                    pdNodes.put(ctx.expr_statement_suffix(),condition);
                }
                else
                    condition=(PDNode)pdNodes.get(ctx.expr_statement_suffix());
                use=false;
                def=false;
                visit(ctx.expr());

                if(iteration==1)
                {
                    exp=new PDNode();
                    exp.setLineOfCode(ctx.expr().getStart().getLine());
                    exp.setCode(getOriginalCodeText(ctx.expr()));
                    analyseDefUse(exp);
                    ddg.addVertex(exp);
                    pdNodes.put(ctx.expr(),exp);
                }
                else
                    exp=(PDNode)pdNodes.get(ctx.expr());
                use=false;
                def=false;
            }
            else {
                def=false;
                use=true;
                visit(ctx.expr());
                if(isfor) {//迭代器循环
                    PDNode condition;
                    if (iteration == 1) {
                        condition = new PDNode();
                        condition.setLineOfCode(ctx.expr().getStart().getLine());
                        condition.setCode(getOriginalCodeText(ctx.expr()));
                        analyseDefUse(condition);
                        ddg.addVertex(condition);
                        pdNodes.put(ctx.expr(), condition);
                    } else
                        condition = (PDNode) pdNodes.get(ctx.expr());
                    if (ctx.expr_statement_suffix().block_params() != null) {
                        condition.addDEF(getOriginalCodeText(ctx.expr_statement_suffix().block_params().expr(0)));
                        RubyField id;
                        if(isclass)
                            id = new RubyField(modifier, getOriginalCodeText(ctx.expr_statement_suffix().block_params().expr(0)));
                        else
                            id = new RubyField(null, getOriginalCodeText(ctx.expr_statement_suffix().block_params().expr(0)));
                        if (!isclass && !ismethod)
                            mainVars.add(id);
                        else
                            localVars.add(id);
                        PDNode params;
                        if (iteration == 1) {
                            params = new PDNode();
                            params.setLineOfCode(ctx.expr_statement_suffix().block_params().getStart().getLine());
                            params.setCode(getOriginalCodeText(ctx.expr_statement_suffix().block_params()));
                            params.addUSE(getOriginalCodeText(ctx.expr_statement_suffix().block_params().expr(0)));
                            analyseDefUse(params);
                            ddg.addVertex(params);
                            pdNodes.put(ctx.expr_statement_suffix().block_params(), params);
                        } else
                            params = (PDNode) pdNodes.get(ctx.expr_statement_suffix().block_params());
                    }
                    def = false;
                    use = false;
                    visit(ctx.expr_statement_suffix().statement_list_terms());
                    expNode(ctx.expr_statement_suffix().statement_list_terms());
                    def = false;
                    use = false;
                    isfor=false;
                }
                else  //对应yield的函数调用情况
                {

                    def=false;
                    use=false;
                    visit(ctx.expr_statement_suffix().statement_list_terms());
                    def=false;
                    use=false;
                }
            }
            Done=true;
            return null;
        }
        @Override
        public String visitClassVar(RubyParser.ClassVarContext ctx)
        {
            if (isUsableExpression(ctx.getText().replace("@",""))) {
                int status = isDefined(ctx.getText());
                if (status < 0) {
                    RubyField id;
                    if(isclass)
                        id = new RubyField(modifier, ctx.getText());
                    else
                        id = new RubyField(null, ctx.getText());
                    activeClasses.peek().addField(id);
                }
                else if(use==true&&status>0)
                {
                    useList.add(ctx.getText());
                }
            }
            Done=true;
            defexp=ctx.getText();
            return null;
        }
        @Override
        public String visitInstanceVar(RubyParser.InstanceVarContext ctx)
        {
            if (isUsableExpression(ctx.getText().replace("@",""))) {
                int status = isDefined(ctx.getText());
                if (status < 0) {
                    RubyField id;
                    if(isclass)
                        id = new RubyField(modifier, ctx.getText());
                    else
                        id = new RubyField(null, ctx.getText());
                    activeClasses.peek().addField(id);
                }
                else if(use==true&&status>=0)
                {
                    useList.add(ctx.getText());
                }
            }
            Done=true;
            defexp=ctx.getText();
            return ctx.getText();
        }
        @Override
        public String visitGlobalVar(RubyParser.GlobalVarContext ctx)
        {
            if(isglobal) {
                if (isUsableExpression(ctx.getText().replace("$", ""))) {
                    int status = isDefined(ctx.getText());
                    if (status < 0) {

                        RubyField id;
                        if (isclass)
                            id = new RubyField(modifier, ctx.getText());
                        else
                            id = new RubyField(null, ctx.getText());
                        globalVars.add(id);
                    }

                }
            }

                int status = isDefined(ctx.getText());
                if(status>=0&&use==true)
                {
                    useList.add(ctx.getText());
                }

            defexp=ctx.getText();
            Done=true;
            return ctx.getText();
        }
        @Override
        public String visitExprArrayAccess(RubyParser.ExprArrayAccessContext ctx)
        {
            if (isUsableExpression(ctx.getText())) {
                int status = isDefined(ctx.getText());
                if (def==true) {
                    if(status < 0) {
                        RubyField id;
                        if (isclass)
                            id = new RubyField(modifier, ctx.getText());
                        else
                            id = new RubyField(null, ctx.getText());
                        if (!isclass && !ismethod)
                            mainVars.add(id);
                        else
                            localVars.add(id);
                    }
                    defexp=ctx.getText();
                }
//                    else if(use==true&&status>=0)
//                    {
//                        useList.add(ctx.getText());
//                    }
                if(use==true&&status>=0)
                {
                    useList.add(ctx.getText());
                }
            }
            return null;
        }
        @Override
        public String visitVarPathId(RubyParser.VarPathIdContext ctx)
        {
            visitChildren(ctx);
            if(!Done) {
                if (isUsableExpression(ctx.getText())) {
                    int status = isDefined(ctx.getText());
                    if (def==true) {
                        if(status < 0) {
                            RubyField id;
                            if (isclass)
                                id = new RubyField(modifier, ctx.getText());
                            else
                                id = new RubyField(null, ctx.getText());
                            if (!isclass && !ismethod)
                                mainVars.add(id);
                            else
                                localVars.add(id);
                        }
                        defexp=ctx.getText();
                    }
//                    else if(use==true&&status>=0)
//                    {
//                        useList.add(ctx.getText());
//                    }
                    if(use==true&&status>=0)
                    {
                        useList.add(ctx.getText());
                    }
                }
            }
            Done=false;
            return ctx.getText();
        }
        @Override
        public String visitIf_tail(RubyParser.If_tailContext ctx)
        {


                if (ctx.ELSIF() != null) {
                    PDNode ifNode;
                    if(iteration==1) {
                    def = false;
                    use = true;
                    visit(ctx.expr());
                    ifNode = new PDNode();
                    ifNode.setLineOfCode(ctx.getStart().getLine());
                    String ifexpr = "if " + getOriginalCodeText(ctx.expr());
                    if (ctx.then_keyword().SEMICOLON() != null)
                        ifexpr += " :";
                    if (ctx.then_keyword().THEN() != null)
                        ifexpr += " then";
                    ifNode.setCode(ifexpr);
                    analyseDefUse(ifNode);
                    ddg.addVertex(ifNode);
                    pdNodes.put(ctx,ifNode);
                }
                    else
                        ifNode=(PDNode)pdNodes.get(ctx);
            }
            use=false;
            def=false;
            if (ctx.ELSIF() != null) {
                visit(ctx.statement_list_terms());
                expNode(ctx.statement_list_terms());
            }
            else
            {
                visit(ctx.else_tail().statement_list_terms());
                expNode(ctx.else_tail().statement_list_terms());
            }
            return null;
        }
        @Override
        public String visitPrimaryBlockUntil(RubyParser.PrimaryBlockUntilContext ctx)
        {
            PDNode whilenode;
            def=false;
            use=true;
            visit(ctx.expr());
            if(iteration==1)
            {
                whilenode=new PDNode();
                whilenode.setLineOfCode(ctx.getStart().getLine());
                String whileexp="until "+getOriginalCodeText(ctx.expr());
                if(ctx.do_keyword().SEMICOLON()!=null)
                    whileexp+=" ;";
                if(ctx.do_keyword().DO()!=null)
                    whileexp+=" do";
                whilenode.setCode(whileexp);
                analyseDefUse(whilenode);
                ddg.addVertex(whilenode);
                pdNodes.put(ctx.expr(),whilenode);

            }
            else
                whilenode=(PDNode) pdNodes.get(ctx);
            def = false;
            use = false;
            visit(ctx.statement_list_terms());
            expNode(ctx.statement_list_terms());
            def = false;
            use = false;
            Done=true;
            return null;
        }
        @Override
        public String visitPrimaryBlockWhile(RubyParser.PrimaryBlockWhileContext ctx)
        {
            PDNode whilenode;
            def=false;
            use=true;
            visit(ctx.expr());
            if(iteration==1)
            {
                 whilenode=new PDNode();
                whilenode.setLineOfCode(ctx.getStart().getLine());
                String whileexp="while "+getOriginalCodeText(ctx.expr());
                if(ctx.do_keyword().SEMICOLON()!=null)
                    whileexp+=" ;";
                if(ctx.do_keyword().DO()!=null)
                    whileexp+=" do";
                whilenode.setCode(whileexp);
                analyseDefUse(whilenode);
                ddg.addVertex(whilenode);
                pdNodes.put(ctx,whilenode);

            }
            else
                whilenode=(PDNode) pdNodes.get(ctx);
            def = false;
            use = false;
            visit(ctx.statement_list_terms());
            expNode(ctx.statement_list_terms());
            def = false;
            use = false;
            Done=true;
            return null;
        }
        @Override
        public String visitCase_body(RubyParser.Case_bodyContext ctx)
        {
            PDNode when;
            if(ctx.WHEN()!=null) {
                for (int i = 0; i < ctx.when_cond().size(); i++) {
                    def = false;
                    use = true;
                    visit(ctx.when_cond(i));
                }
                if (iteration == 1) {
                    when = new PDNode();
                    when.setLineOfCode(ctx.WHEN().getSymbol().getLine());
                    String whenexp = "when " + getOriginalCodeText(ctx.when_cond(0));
                    for (int i = 1; i < ctx.when_cond().size(); i++)
                        whenexp += " " + getOriginalCodeText(ctx.when_cond(i));
                    if (ctx.then_keyword().SEMICOLON() != null)
                        whenexp += " ;";
                    if (ctx.then_keyword().THEN() != null)
                        whenexp += " then";
                    when.setCode(whenexp);
                    analyseDefUse(when);
                    if(!selfadd.equals(""))
                    {
                        when.addUSE(selfadd);
                    }
                    ddg.addVertex(when);
                    pdNodes.put(ctx, when);
                } else
                    when = (PDNode) pdNodes.get(ctx);
                def = false;
                use = false;
                visit(ctx.statement_list_terms());
                expNode(ctx.statement_list_terms());
                def = false;
                use = false;
            }
            else {
                def = false;
                use = false;
                visit(ctx.else_tail());
                def = false;
                use = false;
            }
            return null;
        }

        @Override
        public String visitPrimaryBlockCase1(RubyParser.PrimaryBlockCase1Context ctx)
        {
            PDNode casenode;
            def=false;
            use=true;
            if(ctx.statement_list_terms().statement()!=null) {
                visit(ctx.statement_list_terms().statement());
                expNode(ctx.statement_list_terms());
            }
            if(iteration==1)
            {
                casenode=new PDNode();
                casenode.setLineOfCode(ctx.CASE().getSymbol().getLine());
                if(ctx.statement_list_terms().statement()!=null)
                    if(ctx.DOLLAR()!=null)
                        casenode.setCode("case $"+getOriginalCodeText(ctx.statement_list_terms().statement()));
                    else
                        casenode.setCode("case "+getOriginalCodeText(ctx.statement_list_terms().statement()));
                else
                    casenode.setCode("case");
                analyseDefUse(casenode);
                ddg.addVertex(casenode);
                pdNodes.put(ctx,casenode);
            }
            else
                casenode=(PDNode)pdNodes.get(ctx);
            if(ctx.statement_list_terms().statement()==null)
                selfadd="";
            else
                selfadd=getOriginalCodeText(ctx.statement_list_terms().statement());
            for (int i = 0; i < ctx.case_body().size(); i++) {
                    visit(ctx.case_body(i));
            }
            selfadd="";
            def=false;
            use=false;
            Done=true;
            return null;
        }
        @Override
        public String visitElse_tail(RubyParser.Else_tailContext ctx)
        {
            def=false;
            use=false;
            visit(ctx.statement_list_terms());
            expNode(ctx.statement_list_terms());
            def=false;
            use=false;
            return null;
        }

        @Override
        public String visitPrimaryBlockUnless(RubyParser.PrimaryBlockUnlessContext ctx)
        {
            PDNode ifNode;
            if(iteration==1) {
                def = false;
                use = true;
                visit(ctx.expr());
                ifNode = new PDNode();
                ifNode.setLineOfCode(ctx.getStart().getLine());
                String ifexpr = "unless " + getOriginalCodeText(ctx.expr());
                if (ctx.then_keyword().SEMICOLON() != null)
                    ifexpr += " :";
                if (ctx.then_keyword().THEN() != null)
                    ifexpr += " then";
                ifNode.setCode(ifexpr);
                analyseDefUse(ifNode);
                ddg.addVertex(ifNode);
                pdNodes.put(ctx,ifNode);
                use = false;
                def = false;
            }
            else
                ifNode=(PDNode)pdNodes.get(ctx);
            visit(ctx.statement_list_terms());
            expNode(ctx.statement_list_terms());
            if(ctx.else_tail()!=null)
                visit(ctx.else_tail());
            Done=true;
            return null;
        }
        @Override
        public String visitPrimaryBlockIf(RubyParser.PrimaryBlockIfContext ctx)
        {
            PDNode ifNode;
            if(iteration==1) {
                def = false;
                use = true;
                visit(ctx.expr());
                ifNode = new PDNode();
                ifNode.setLineOfCode(ctx.getStart().getLine());
                String ifexpr = "if " + getOriginalCodeText(ctx.expr());
                if (ctx.then_keyword().SEMICOLON() != null)
                    ifexpr += " :";
                if (ctx.then_keyword().THEN() != null)
                    ifexpr += " then";
                ifNode.setCode(ifexpr);
                analyseDefUse(ifNode);
                ddg.addVertex(ifNode);
                pdNodes.put(ctx,ifNode);
                use = false;
                def = false;
            }
            else
                ifNode=(PDNode)pdNodes.get(ctx);
            visit(ctx.statement_list_terms());
            expNode(ctx.statement_list_terms());
            if(!ctx.if_tail().isEmpty())
            {
                for (int i = 0; i < ctx.if_tail().size(); i++) {         //这里有疑问，else后面不用加endif吗
                    visit(ctx.if_tail(i));
                }
            }
            Done=true;
            return null;
        }
        @Override
        public String visitExprPrint(RubyParser.ExprPrintContext ctx)
        {
//            PDNode puts;
            def = false;
            use = true;
//            for(int i=0;i<ctx.primary().size();i++)
//                visit(ctx.primary(i));

            visit(ctx.expr());

//            if(iteration==1) {
//                puts = new PDNode();
//                puts.setLineOfCode(ctx.getStart().getLine());
//                puts.setCode(getOriginalCodeText(ctx));
//                analyseDefUse(puts);
//                ddg.addVertex(puts);
//                pdNodes.put(ctx,puts);
//            }
//            else
//                puts=(PDNode)pdNodes.get(ctx);

//            use = false;
            return null;
        }
        @Override
        public String visitExprDotRef(RubyParser.ExprDotRefContext ctx)
        {
            if(!getOriginalCodeText(ctx.expr(1)).equals("each")&&!getOriginalCodeText(ctx.expr(1)).equals("times")
                    &&!getOriginalCodeText(ctx.expr(1)).equals("new"))
            {
                def=false;
                use=true;
                visit(ctx.expr(0));
            }
            if(getOriginalCodeText(ctx.expr(1)).equals("each")||getOriginalCodeText(ctx.expr(1)).equals("times"))
                isfor=true;
            return null;
        }
        @Override
        public String visitExprAssign(RubyParser.ExprAssignContext ctx)
        {
            def=true;
            use=true;
            isglobal=true;
            visit(ctx.expr(0));
            isglobal=false;
            def=false;
            defList.add(defexp);
            use=true;
            visit(ctx.expr(1));
            return null ;
        }
        @Override
        public String visitExprBatchAssign(RubyParser.ExprBatchAssignContext ctx)
        {

            def=true;
            use=false;
            isglobal=true;
            visit(ctx.expr(0));
            isglobal=false;
            def=false;
            defList.add(defexp);
            use=true;
            visit(ctx.expr(1));
            return null ;
        }
        @Override
        public String visitRescuestatement(RubyParser.RescuestatementContext ctx)
        {
            if(ctx.rescure_param()!=null) {
                PDNode rescue;


                if (iteration == 1) {
                    rescue = new PDNode();
                    rescue.setLineOfCode(ctx.getStart().getLine());
                    rescue.setCode("rescue " + getOriginalCodeText(ctx.rescure_param()));

                    ddg.addVertex(rescue);
                    pdNodes.put(ctx,rescue);
                }
                else
                    rescue=(PDNode) pdNodes.get(ctx);
                changed|=rescue.addDEF(ctx.rescure_param().hash_asso().expr(1).getText());
                RubyField exceptionVar;
                if(isclass)
                    exceptionVar = new RubyField(modifier, ctx.rescure_param().hash_asso().expr(1).getText());
                else
                    exceptionVar = new RubyField(null, ctx.rescure_param().hash_asso().expr(1).getText());
                localVars.add(exceptionVar);
                def=false;
                use=false;
                visit(ctx.statement_list_terms());
                expNode(ctx.statement_list_terms());
                def=false;
                use=false;
                localVars.remove(exceptionVar);
                return null;

            }
            def=false;
            use=false;
            visit(ctx.statement_list_terms());
            expNode(ctx.statement_list_terms());
            def=false;
            use=false;
            if(ctx.else_tail()!=null)
            {
                visit(ctx.else_tail().statement_list_terms());
                expNode(ctx.else_tail().statement_list_terms());
                def=false;
                use=false;
            }
            return null;
        }

        @Override
        public String visitPrimaryBeginBlock(RubyParser.PrimaryBeginBlockContext ctx)
        {
            PDNode begin;
            def=false;
            use=false;
            visit(ctx.statement_list_terms(0));
            expNode(ctx.statement_list_terms(0));
            def=false;
            use=false;
            if(ctx.rescuestatement()!=null)
                visit(ctx.rescuestatement());
            def=false;
            use=false;
            if(ctx.ensure()!=null)
            {
                visit(ctx.statement_list_terms(1));
                expNode(ctx.statement_list_terms(1));
            }
            Done=true;
            return null;
        }
        @Override
        public String visitFunction_definition(RubyParser.Function_definitionContext ctx)
        {
            PDNode entry;
            if(iteration==1)
            {
                entry = new PDNode();
                entry.setLineOfCode(ctx.getStart().getLine());
                String retType = "auto";
                if (ctx.function_definition_header().expr() != null)
                    retType = ctx.function_definition_header().expr().getText().replace("->","");
                entry.setCode(getOriginalCodeText(ctx.function_definition_header()).replace("\r\n",""));
                entry.setProperty("name", ctx.function_definition_header().function_name().getText());
                entry.setProperty("type", retType);
                ddg.addVertex(entry);
                pdNodes.put(ctx, entry);
                if(ctx.function_definition_header().function_definition_params()!=null) {
                    methodParams = new RubyField[ctx.function_definition_header().function_definition_params().function_definition_param().size()];
                    for (int i = 0; i < methodParams.length; ++i) {
                        if (isclass)
                            methodParams[i] = new RubyField(modifier,
                                    ctx.function_definition_header().function_definition_params().function_definition_param(i).getText());
                        else
                            methodParams[i] = new RubyField(null,
                                    ctx.function_definition_header().function_definition_params().function_definition_param(i).getText());
                    }
                    entry.setProperty("params", methodParams);
                    for (int i = 0; i < methodParams.length; ++i)
                        changed |= entry.addDEF(methodParams[i].NAME);
                }
            }
            else {
                entry = (PDNode) pdNodes.get(ctx);
                if(ctx.function_definition_header().function_definition_params()!=null)
                    methodParams = (RubyField[]) entry.getProperty("params");

            }
            methodDefInfo = findDefInfo((String) entry.getProperty("name"),
                    (String) entry.getProperty("type"), methodParams);
            ismethod=true;
            localVars.clear();
            def=false;
            use=false;
            visit(ctx.statement_list_terms());
            expNode(ctx.statement_list_terms());
            def=false;
            use=false;
            localVars.clear();
            methodParams = new RubyField[0];
            ismethod = false;
            Done=true;
            return null;
        }
        @Override
        public String visitPrimaryStatementReturn(RubyParser.PrimaryStatementReturnContext ctx)
        {
            def=false;
            use=true;
            visit(ctx.expr());
            def=false;
            use=false;
            return null;
        }
        @Override
        public String visitAlias_statement(RubyParser.Alias_statementContext ctx)
        {
            def=false;
            use=true;
            visit(ctx.function_name_or_symbol(0));
            def=true;
            use=false;
            visit(ctx.function_name_or_symbol(1));
            defList.add(ctx.function_name_or_symbol(1).getText());
            return null;
        }
        @Override
        public String visitUndef_statement(RubyParser.Undef_statementContext ctx)
        {
            def=false;
            use=true;
            for(int i=0;i<ctx.function_name_or_symbol().size();i++)
                visit(ctx.function_name_or_symbol(0));
            def=false;
            use=false;
            return null;
        }
        @Override
        public String visitModule_definition(RubyParser.Module_definitionContext ctx)
        {
            localVars.clear();
            visit(ctx.statement_list_terms());
            expNode(ctx.statement_list_terms());
            localVars.clear();
            Done=true;
            return null;
        }
         @Override
         public String visitClass_definition(RubyParser.Class_definitionContext ctx)
         {
             if(ctx.class_header().BIT_SHL()==null) {
                 for (RubyClass cls : classInfos) {
                     if (cls.name.equals(ctx.class_header().cpath().getText())) {
                         activeClasses.push(cls);
                         isclass = true;
                         modifier = "public";
                         visit(ctx.statement_list_terms());
                         activeClasses.pop();
                         isclass = false;    //不必清空localvar，因为类里只要classvar和instancevar
                         break;
                     }
                 }
             }
             else
             {
                 PDNode clsname;
                 def=false;
                 use=true;
                 visit(ctx.class_header().identifier());
                 if(iteration==1)
                 {
                     clsname=new PDNode();
                     clsname.setLineOfCode(ctx.class_header().getStart().getLine());
                     clsname.setCode(getOriginalCodeText(ctx));
                     analyseDefUse(clsname);
                     ddg.addVertex(clsname);
                     pdNodes.put(ctx,clsname);
                 }
                 else
                     clsname=(PDNode) pdNodes.get(ctx);
                 localVars.clear();
                 visit(ctx.statement_list_terms());
                 expNode(ctx.statement_list_terms());
                 localVars.clear();
             }
             Done=true;
             return null;
         }

        @Override
        public String visitPrimaryModifier(RubyParser.PrimaryModifierContext ctx)
        {
            modifier=ctx.getText();
            return null;
        }
        @Override
        public String visitStatement(RubyParser.StatementContext ctx)
        {
            def=false;
            use=true;
            visitChildren(ctx);
            return null;
        }
        @Override
        public String visitStatement_list_terms(RubyParser.Statement_list_termsContext ctx)
        {
            visitChildren(ctx);
            if(!Done)
            {
                if(ctx.statement()!=null) {
                    expr += getOriginalCodeText(ctx.statement());
                    if (ctx.terms().term().isEmpty()) {
                        expr += " ";
                    } else {
                        PDNode exp;
                        if (iteration == 1) {
                             exp = new PDNode();
                        exp.setLineOfCode(ctx.terms().getStart().getLine());
                        exp.setCode(expr);
                        expr = "";
                        use = false;
                        analyseDefUse(exp);
                        ddg.addVertex(exp);
                        pdNodes.put(ctx,exp);
                    }
                        else
                             exp=(PDNode) pdNodes.get(ctx);
                    }
                }
            }
            else {
                use=false;
                Done = false;
            }
            return null;
        }

        public void expNode(RubyParser.Statement_list_termsContext ctx)
        {
            if(!expr.equals("")) {
                PDNode exp ;
                if(iteration==1) {
                    exp = new PDNode();
                    exp.setLineOfCode(ctx.terms().getStart().getLine());
                    exp.setCode(expr);
                    expr = "";
                    use = false;
                    analyseDefUse(exp);
                    ddg.addVertex(exp);
                    pdNodes.put(ctx, exp);
                }
                else
                    exp=(PDNode) pdNodes.get(ctx);
                use=false;
                Done = false;
            }
        }
    }
}
class MethodDefInfo {
    public final String NAME;                   // method name
    public final String RET_TYPE;               // method return type
    public final String CLASS_NAME;             // belong to which class name

    // DEF Info
    private boolean stateDEF;   //判断有没有这个方法
    private boolean[] argDEFs;
    private List<String> fieldDEFs;         // including the self var

    public MethodDefInfo(String ret, String name, String cls,String[] args) {
        NAME = name;
        RET_TYPE = ret;
        CLASS_NAME = cls;
        //
        fieldDEFs = new ArrayList<>();
        stateDEF = guessByTypeOrName();
        argDEFs=new boolean[args.length];
    }

    private boolean guessByTypeOrName() {
        // First check if this method is a constructor ...
        if (RET_TYPE == null)
            return true;
        // If not, then try to guess by method-name ...
        String[] prefixes = {"set", "put", "add", "insert", "push", "append"};
        for (String pre : prefixes)
            if (NAME.toLowerCase().startsWith(pre))
                return true;
        return false;
    }

    public boolean doesStateDEF() {
        return stateDEF;
    }

    public void setStateDEF(boolean stateDef) {
        stateDEF = stateDef;
    }

    public boolean[] argDEFs() {
        return argDEFs;
    }

    public void setArgDEF(int argIndex, boolean def) {
        argDEFs[argIndex] = def;
    }

    public void setAllArgDEFs(boolean[] argDefs) {
        argDEFs = argDefs;
    }

    public String[] fieldDEFs() {
        return fieldDEFs.toArray(new String[fieldDEFs.size()]);
    }

    public void addFieldDEF(String fieldName) {
        if (!fieldDEFs.contains(fieldName)) {
            fieldDEFs.add(fieldName);
            stateDEF = true;
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof MethodDefInfo))
            return false;
        MethodDefInfo info = (MethodDefInfo) obj;
        return this.NAME.equals(info.NAME) && this.CLASS_NAME.equals(info.CLASS_NAME)
                && this.RET_TYPE.equals(info.RET_TYPE);
    }

    @Override
    public String toString() {
        String retType = RET_TYPE == null ? "null" : RET_TYPE;
        StringBuilder str = new StringBuilder();
        str.append("{ TYPE : \"").append(retType).append("\", ");
        str.append("NAME : \"").append(NAME).append("\", ");
        str.append("CLASS : \"").append(CLASS_NAME).append("\", ");
        return str.toString();
    }
}
